﻿/**
 * Created by Weizehua on 2015年12月14日, 0014.
 */

var chrome = require('chrome');
var Cu = chrome.Cu;
Cu.import("resource://gre/modules/Services.jsm");

var preferencesService = require("sdk/preferences/service");
var cookieManager = Services.cookies;
var timers = require("sdk/timers");
var setTimeout = timers.setTimeout;
var clearTimeout = timers.clearTimeout;
var setInterval = timers.setInterval;
var clearInterval = timers.clearInterval;

var common = require("common");
var converter;
var tabs = require("tabs");

var proxy = require("proxy");

var hubWorker = {};
var logger;
var log;
var error;

exports.init = function (_converter, _logger)
{
    converter = _converter;
    initHub();
    logger = _logger;
    log = logger.log;
    error = logger.error;
};
function initHub()
{
    tabs.openHidden(common.blankPath, onHubOpened, common.hubPath);

    function onHubOpened(tab)
    {
        hubWorker = tab.defaultWorker;

        // listen for hub message
        hubWorker.port.on("hub", onHubMessage);
    }
}

function onHubMessage(packedData)
{
    var info = converter.unpack(packedData);
    onServerMessage(info.type, info.args);
}

function callServerFunction(id, args)
{
    sendToServer("serverFunctionCalled", { id: id, args: args });
}

function onServerFunctionCalled(id, args)
{
    callServerFunction(id, args);
}
exports.onServerFunctionCalled = onServerFunctionCalled;

function onScriptFinished(id, result)
{
    sendToServer("scriptFinished", {
        id: id,
        result: result
    });
}
exports.onScriptFinished = onScriptFinished;

function sendToServer(_type, args)
{
    hubWorker.port.emit("hub", converter.pack({ type: _type, args: args }));
}

function onServerMessage(type, args)
{
    if (type === "runScript")
    {
        return runScript(args.id, args.content);
    }
    if (type === "runFunction")
    {
        return runFunction(args.id, args.name, args.args);
    }
    if (type === "runPageScript")
    {
        return runPageScript(args.tab, args.id, args.content);
    }
    if (type === "runPageFunction")
    {
        return runPageFunction(args.tab, args.id, args.name, args.args);
    }
}

function runScript(id, content)
{
    var result = eval(content);

    onScriptFinished(id, result);
}

function runFunction(id, name, args)
{
    var fn;
    if ('undefined' !== typeof fs && fs[name] instanceof Function)
        fn = fs[name];
    else
        fn = eval(name);
    var result = fn.apply(null, args);

    onScriptFinished(id, result);
}

function runPageScript(tab, id, content)
{
    var worker = tab.defaultWorker;
    worker.port.emit("runScript", id, content);
}

function runPageFunction(tab, id, name, args)
{
    var worker = tab.defaultWorker;
    worker.port.emit("runFunction", id, name, args);
}

var serverFunctionRegexp = /ServerJsFunctionWarpper:(\d+)/;
function makeCallable(obj)
{
    var type = typeof obj;
    if (type === "string")
    {
        if (serverFunctionRegexp.test(obj))
        {
            var id = serverFunctionRegexp.exec(obj)[1];
            return function (args)
            {
                callServerFunction(id, args);
            };
        }
        else if (converter.isObjectString(obj))
        {
            var converted = converter.at(obj);
            if (typeof converted === "function")
                return converter.at(obj);
        }
    }
    else if (type === "function")
    {
        return obj;
    }
    else if (!obj)
    {
        return function ()
        {
        };
    }
    throw "function makeCallable() : this is not a callable object!";
}
exports.makeCallable = makeCallable;


//----------------------------------------------------------------------------
// Server Functions
//----------------------------------------------------------------------------
fs = 'undefined' !== typeof fs && fs || {};
function test()
{
    //testSubSearch();
}
fs.test = exports.test = test;

function testSubSearch()
{
    var subItemBrowseParam = {
        number: 3,
        top: true,
        mid: true,
        bottom: true,
        rates: true,
        trades: true,
        collect: true
    };
    searchSubItems({
        browseParam: subItemBrowseParam,
        link: "https://shop111711031.taobao.com/search.htm?spm=2013.1.w4023-10567681280.18.rTBq2E&orderType=hotkeep_desc",
        onFinished: onSubItemTaskFinished,
        onError: onSubItemTaskFailed
    });
    function onSubItemTaskFinished()
    {
        log("sub search finished");
    }
    function onSubItemTaskFailed()
    {
        log("sub search failed");
    }
}

function testBrowseTaobao()
{
    var searchParam = {
        keyword: "足球",
        lowerPrice: 80,
        higherPrice: 120,
        location: '上海',
        sortMethod: 'defaultSort',
        title: null,
        price: null,
        shopName: '世达',
        itemId: null
    };
    var mainItemBrowseParam = {
        number: 1,
        top: true,
        mid: true,
        bottom: true,
        rates: true,
        trades: true,
        collect: true
    };
    var subItemBrowseParam = {
        number: 1,
        top: true,
        mid: true,
        bottom: true,
        rates: true,
        trades: true,
        collect: true
    };
    var compareItemBrowseParam = {
        number: 1,
        top: true,
        mid: true,
        bottom: true,
        rates: true,
        trades: true,
        collect: true
    };
    browseTaobao(searchParam, mainItemBrowseParam, subItemBrowseParam, compareItemBrowseParam,
        onFinished, onError, null);
    function onFinished()
    {
        log('browsing finished!');
    }
    function onError(reason)
    {
        log("browse error : " + reason);
    }
}

function testSearchTaobao()
{
    searchTaobao({
        keyword: "足球",
        lowerPrice: 80,
        higherPrice: 120,
        location: '上海',
        sortMethod: 'defaultSort',
        title: null,
        price: null,
        shopName: '世达',
        itemId: null,
        searchTarget : "taobao"
    }, onFound, onError, 1000 * 100);
    function onFound(title)
    {
        logger.info('Item found : ' + title);
    }
    function onError(reason)
    {
        logger.info('Item not found : ' + reason);
    }
}

function quickLogin(username, _onOk, _onError, timeout)
{
    timeout = timeout || 60000;

    var onOk = makeCallable(_onOk);
    var onError = makeCallable(_onError);

    tabs.open({
        url: "http://login.taobao.com",
        onOpen: onOpen,
        onReady: null,
        scripts: "./Taobao/QuickLogin.js"
    });

    var thetab;
    function onOpen(tab)
    {
        thetab = tab;
        logger.log("quickLogin()->onOpen()");
        tab.tasks.onQuickLoginPage = onLoginPage;
        tab.tasks.onITaobaoPage = onLoginOkPage;
        tab.tasks.onTaobaoPage = onLoginOkPage;
        tab.tasks.quickLoginFailed = onLoginFailed;
        function onLoginPage()
        {
            logger.log("quickLogin()->onOpen()->onLoginPage()");
            tab.send("quickLogin", username);
        }

        function onLoginOkPage(loginUsername)
        {
            logger.log("quickLogin()->onOpen()->onLoginOkPage()");
            if (loginUsername === username)
                okHandler();
            else
                failedHandler("already loged in");
        }

        function onLoginFailed(reason)
        {
            logger.log("quickLogin()->onOpen()->onLoginFailed()");
            failedHandler(reason);
        }
    }

    function okHandler()
    {
        onOk();
        thetab.close();
    }
    function failedHandler(reason)
    {
        onError(reason);
        thetab.close();
    }
}
fs.quickLogin = quickLogin;

//---------------------------------------------
// Firefox handling
//---------------------------------------------
function ping()
{
    return true;
}
fs.ping = ping;

function clearCookies()
{
    Services.cookies.removeAll();
    return true;
}
fs.clearCookies = clearCookies;

function cookieLength(name, path, host)
{
    var c = cookie(name, path, host);
    return c ? c.length : null;
}
fs.cookieLength = cookieLength;

function cookie(name, path, host)
{
    if (!cookieManager.cookieExists({ name: name, path: path, host: host }))
        return null;

    var cookies = cookieManager.getCookiesFromHost(host);
    while (cookies.hasMoreElements())
    {
        var cookie = cookies.getNext();
        if (cookie.name === name && cookie.path === path)
            return cookie.value.length;
    }
    return null;
}
fs.cookie = cookie;

function setUserAgent(ua)
{
    preferencesService.set("general.useragent.override", ua);
}
fs.setUserAgent = setUserAgent;

function setHttpProxy(host, port)
{
    proxy.setHttpProxy(host, port);
}
fs.setHttpProxy = setHttpProxy;

function enableProxy()
{
    proxy.enableProxy();
}
fs.enableProxy = enableProxy;

function disableProxy()
{
    proxy.disableProxy();
}
fs.disableProxy = disableProxy;


//---------------------------------------------
// tabs handling
//---------------------------------------------
function openTab(url, onOpen)
{
    tabs.open({
        url: url, onOpen: function (tab)
        {
            converter.convert(tab);
            makeCallable(onOpen)(tab);
        }
    });
}
fs.openTab = openTab;

function isTabReady(tab)
{
    return tab.info && tab.info.ready;
}
fs.isTabReady = isTabReady;

function waitForTabReady(tab, onReady, onError, maxtimeout)
{
    maxtimeout = maxtimeout || 120 * 1000;
    var end = new Date();
    end.setMilliseconds(end.getMilliseconds() + maxtimeout);
    var timer = setInterval(check, 100);
    setTimeout(check, 10);
    function check()
    {
        if (isTabReady(tab))
        {
            makeCallable(onReady)();
            clearInterval(timer);
        }
        else if (new Date() > end || !tab.info.opened)
        {
            clearInterval(timer);
            makeCallable(onError);
        }
    }
}
fs.waitForTabReady = waitForTabReady;

function closeTab(tab)
{
    tab.close();
}
fs.closeTab = closeTab;

function sourceLength(tab, onLengthGot)
{
    source(tab, function (content)
    {
        makeCallable(onLengthGot)(content.length);
    });
}
fs.sourceLength = sourceLength;

function source(tab, onGot)
{
    var worker = tab.attach({ contentScript: "self.port.emit('ScriptContent', document.body.textContent)" });
    worker.port.once("ScriptContent", onSource);
    function onSource(content)
    {
        worker.destroy();
        makeCallable(onGot)(content);
    }
}
fs.source = source;

//--------------------------------------------------------
// TSearcher Extras
//--------------------------------------------------------


function searchTaobao(searchParam, _onFound, _onFailed, timeout)
{
    timeout = timeout || 1000 * 120;
    var found = makeCallable(_onFound);
    var onError = makeCallable(_onFailed);
    switch(searchParam.searchTarget)
    {
        case "taobao":
            tabs.open({
                url: "http://www.taobao.com",
                onOpen: onOpen,
                onReady: null,
                scripts: "./Taobao/searchTaobao.js"
            });
            break;
        case "tmall":
            tabs.open({
                url: "http://www.tmall.com",
                onOpen: onOpen,
                onReady: null,
                scripts: "./Taobao/searchTmall.js"
            });
            break;
        default:
            tabs.open({
                url: "https://mosaic.re.taobao.com/search?keyword=" + searchParam.keyword + "&_input_charset=utf-8",
                onOpen: onOpen,
                onReady: null,
                scripts: "./Taobao/searchTBcc.js"
            });
            break;
    }

    var timer = setTimeout(onTimeout, timeout);

    function onOpen(tab)
    {
        tab.tasks.indexOpened = function ()
        {
            tab.send('searchIt', { itemKeyword: searchParam.keyword });
        };
        tab.tasks.searchResultShown = function ()
        {
            tab.send('lookupItem', {
                itemKeyword: searchParam.keyword,
                sortBy: searchParam.sortMethod,
                itemLowerPrice: searchParam.lowerPrice,
                itemHigherPrice: searchParam.higherPrice,
                location: searchParam.location,
                itemTitle: searchParam.title,
                itemPrice: searchParam.price,
                itemShopName: searchParam.shopName,
                itemId: searchParam.itemId,
                maxNextPages: searchParam.maxNextPages
            });
        };
        tab.tasks.itemsFound = function (items)
        {
            log('itemsFound : ' + items.length);
        };
        tab.tasks.mainItemFound = function (item)
        {
            log('mainItemFound : ' + item.title);
            found(converter.convert(item));

            if (searchParam.closeOnFinished)
                tab.close()
            clearTimeout(timer);
        };
        tab.tasks.compareItemSelected = function ()
        {
            tab.send('searchNextItem');
        };
        tab.tasks.error = function (reason)
        {
            if (searchParam.closeOnError)
                tab.close()
            onError(reason);
        };
    }
    function onTimeout()
    {
        if (searchParam.closeOnError)
            tab.close();
        onError('timeout');
    }
}
fs.searchTaobao = searchTaobao;

function browseTaobao(searchParam,
    mainItemsParam, subItemsParam, compareItemsParam,
    _onFinished, _onFailed, timeout)
{
    timeout = timeout || 15 * 60 * 1000;
    var onFinished = makeCallable(_onFinished);
    var onError = makeCallable(_onFailed);

    switch (searchParam.searchTarget)
    {
        case "taobao":
            tabs.open({
                url: "http://www.taobao.com",
                onOpen: onOpen,
                onReady: null,
                scripts: "./Taobao/searchTaobao.js"
            });
            break;
        case "tmall":
            tabs.open({
                url: "http://www.tmall.com",
                onOpen: onOpen,
                onReady: null,
                scripts: "./Taobao/searchTmall.js"
            });
            break;
        default:
            tabs.open({
                url: "https://mosaic.re.taobao.com/search?keyword=" + searchParam.keyword + "&_input_charset=utf-8",
                onOpen: onOpen,
                onReady: null,
                scripts: "./Taobao/searchTBcc.js"
            });
            break;
    }
    var tab;

    var browseInfo = {
        mainBrowsed: 0,
        subBrowsed: 0,
        compareBrowsed: 0
    };

    var timer = setTimeout(onTimeout, timeout);

    function onOpen(_tab)
    {
        tab = _tab;
        tab.tasks.indexOpened = function ()
        {
            tab.send('searchIt', { itemKeyword: searchParam.keyword });
        };
        tab.tasks.searchResultShown = function ()
        {
            tab.send('lookupItem', {
                itemKeyword: searchParam.keyword,
                sortBy: searchParam.sortMethod,
                itemLowerPrice: searchParam.lowerPrice,
                itemHigherPrice: searchParam.higherPrice,
                location: searchParam.location,
                itemTitle: searchParam.title,
                itemPrice: searchParam.price,
                itemShopName: searchParam.shopName,
                itemId : searchParam.itemId,
                maxNextPages: searchParam.maxNextPages
            });
        };
        tab.tasks.itemsFound = function (items)
        {
            log('itemsFound : ' + items.length);
        };
        tab.tasks.mainItemFound = function (item)
        {
            log('mainItemFound : ' + item.title);
            if (browseInfo.mainBrowsed < mainItemsParam.number)
            {
                var subitemslink;
                // browse it
                browseItem({
                    browseParam: mainItemsParam,
                    link: item.link,
                    onFinished: onItemBrowseFinished,
                    onError: onBrowseFailed,
                    onSubItemsLink: onSubItemsLinkGot
                });
            }
            else
                checkAndContinue();
            function onSubItemsLinkGot(arg)
            {
                subitemslink = arg.link;
            }
            function onItemBrowseFinished()
            {
                browseInfo.mainBrowsed++;
                if (browseInfo.subBrowsed < subItemsParam.number)
                {
                    searchSubItems({
                        browseParam: subItemsParam,
                        link: subitemslink,
                        onFinished: onSubItemTaskFinished,
                        onError: onSubItemTaskFailed
                    });
                }
                else
                    checkAndContinue();
                function onSubItemTaskFinished()
                {
                    browseInfo.subBrowsed = subItemsParam.number;
                    checkAndContinue();
                }
                function onSubItemTaskFailed(reason)
                {
                    onError(reason);
                }
            }
            function onBrowseFailed(reason)
            {
                throw 'Browse Main Item Failed with Error :' + reason;
            }
        };
        tab.tasks.compareItemSelected = function (item)
        {
            if (browseInfo.compareBrowsed < compareItemsParam.number)
            {
                // browse it
                browseItem({
                    browseParam: compareItemsParam,
                    link: item.link,
                    onFinished: onItemBrowseFinished,
                    onError: onBrowseFailed
                });
            }
            else
                checkAndContinue();
            function onItemBrowseFinished()
            {
                browseInfo.compareBrowsed++;
                checkAndContinue();
            }
            function onBrowseFailed(reason)
            {
                throw 'Browse Main Item Failed with Error :' + reason;
            }
        };
        tab.tasks.error = function (reason)
        {
            if (searchParam.closeOnError)
                tab.close();
            onError(reason);
        };
    }
    function checkAndContinue()
    {
        if (!isBrowseFinished())
        {
            askForNextSearch();
        }
        else
        {
            onBrowseFinished();
        }
    }
    function isBrowseFinished()
    {
        return browseInfo.mainBrowsed >= mainItemsParam.number
        && browseInfo.subBrowsed >= subItemsParam.number
        && browseInfo.compareBrowsed >= compareItemsParam.number;
    }
    function askForNextSearch()
    {
        tab.send('searchNextItem');
    }

    function onBrowseFinished()
    {
        if (searchParam.closeOnFinished)
            tab.close();
        onFinished();
    }

    function onTimeout()
    {
        if (searchParam.closeOnError)
            tab.close();
        onError('timeout');
    }
}
fs.browseTaobao = browseTaobao;

function browseItem(_browseParam, _link, _onFinished, _onError, _timeout, _onSubItemsLink)
{
    var browseParam;
    var link;
    var onFinished;
    var onError;
    var timeout;
    var onSubItemsLink;
    if (_browseParam instanceof Object)
    {
        browseParam = _browseParam.browseParam;
        link = _browseParam.link;
        onFinished = _browseParam.onFinished;
        onError = _browseParam.onError;
        timeout = _browseParam.timeout;
        onSubItemsLink = _browseParam.onSubItemsLink;
    }
    else
    {
        browseParam = _browseParam;
        link = _link;
        onFinished = _onFinished;
        onError = _onError;
        timeout = _timeout;
        onSubItemsLink = _onSubItemsLink;
    }
    browseParam.stepInterval = 'undefined' === typeof browseParam.stepInterval || 4000;
     
    // parse browseParam
    var stepHints = {
        top: 'goTop',
        mid: 'goMid',
        bottom: 'goBottom',
        rates: 'goRates',
        trades: 'goTrades',
        collect: 'goCollect'
    };
    var steps = [];
    for (var key in stepHints)
    {
        if (browseParam[key])
            steps.push(stepHints[key]);
    }
    if (onSubItemsLink)
        steps.push('getSubItemsLink');

    // fix param
    timeout = timeout || 5 * 60 * 1000;
    onFinished = makeCallable(onFinished);
    onError = makeCallable(onError);
    onSubItemsLink = makeCallable(onSubItemsLink);

    tabs.open({
        url: link,
        onOpen: onOpen,
        onReady: null,
        scripts: "./Taobao/browseTaobao.js"
    });
    var timer = setTimeout(onTimeout, timeout);

    var tab;
    function onOpen(_tab)
    {
        tab = _tab;

        tab.tasks.itemTabLoaded = onStepOk;
        tab.tasks.topArrived = onStepOk;
        tab.tasks.midArrived = onStepOk;
        tab.tasks.bottomArrived = onStepOk;
        tab.tasks.ratesArrived = onStepOk;
        tab.tasks.tradesArrived = onStepOk;
        tab.tasks.collectItemArrived = onStepOk;

        tab.tasks.subItemsLink = function (arg)
        {
            onSubItemsLink(arg);
            autoStep();
        };

        tab.tasks.error = onBrowseError;
    }
    function hasNextStep()
    {
        log("hasNextStep()->steps.length : " + steps.length);
        log("steps[0] : " + steps[0]);
        return steps.length > 0;
    }
    function nextStep()
    {
        tab.send(steps.shift(), { secondsPerScreen: browseParam.secondsPerScreen });
    }
    function autoStep()
    {
        if (hasNextStep())
        {
            nextStep();
        }
        else
        {
            onBrowseFinished()
        }
    }
    function onStepOk()
    {
        setTimeout(autoStep, browseParam.stepInterval);
    }

    function onTimeout()
    {
        onBrowseError('timeout')
    }
    function onBrowseFinished()
    {
        if (browseParam.closeOnFinished)
            tab.close();
        onFinished();
    }
    function onBrowseError(reason)
    {
        if (browseParam.closeOnError)
            tab.close();
        onError('timeout');
    }
}

function searchSubItems(option)
{
    var browseParam;
    var link;
    var onFinished;
    var onError;
    var timeout;

    browseParam = option.browseParam;
    link = option.link;
    onFinished = option.onFinished;
    onError = option.onError;
    timeout = option.timeout;

    // fix params
    onFinished = makeCallable(onFinished);
    onError = makeCallable(onError);
    timeout = timeout || 1000 * 60 * 15;


    tabs.open({
        url: link,
        onOpen: onOpen,
        onReady: null,
        scripts: "./Taobao/searchSubItems.js"
    });
    var tab;

    var browsedNumber = 0;

    var timer = setTimeout(onTimeout, timeout);

    function onOpen(_tab)
    {
        tab = _tab;

        tab.tasks.searchResultShown = function ()
        {
            tab.send('lookupItem', {
                maxNextPages: 0
            });
        };
        tab.tasks.itemsFound = function (items) { };
        tab.tasks.mainItemFound = function (item)
        {
            if (browsedNumber < browseParam.number)
            {
                // browse it
                browseItem({
                    browseParam: browseParam,
                    link: item.link,
                    onFinished: onItemBrowseFinished,
                    onError: onBrowseFailed
                });
            }
            else
                checkAndContinue();
            function onItemBrowseFinished()
            {
                browsedNumber++;
                checkAndContinue();
            }
            function onBrowseFailed(reason)
            {
                throw 'Browse Main Item Failed with Error :' + reason;
            }
        };
        tab.tasks.compareItemSelected = function (item) { };
        tab.tasks.error = function (reason)
        {
            tab.close();
            onError(reason);
        };
    }
    function checkAndContinue()
    {
        if (!isBrowseFinished())
        {
            askForNextSearch();
        }
        else
        {
            onBrowseFinished();
        }
    }
    function isBrowseFinished()
    {
        return browsedNumber >= browseParam.number;
    }
    function askForNextSearch()
    {
        tab.send('searchNextItem');
    }

    function onBrowseFinished()
    {
        tab.close();
        onFinished();
    }

    function onTimeout()
    {
        tab.close();
        onError('timeout');
    }
}